iT邦幫忙

2024 iThome 鐵人賽

DAY 5
2
Security

資安這條路:系統化學習網站安全與網站滲透測試系列 第 5

資安這條路:Day5 深入探索HTTP請求與回應:安全性與身分驗證的關鍵概念

  • 分享至 

  • xImage
  •  

前言

本篇文章將針對 HTTP 協定進行深入探討,並設計實作實驗(lab)來幫助讀者更好地理解這個重要的網路協定。
文章將涵蓋以下三個關鍵主題:

  1. 請求方法:介紹各種 HTTP 請求方法,包括它們的用途和特點,幫助讀者掌握如何與伺服器進行互動。
  2. 狀態碼:分析 HTTP 狀態碼的不同類別,解釋它們在請求處理過程中所扮演的角色,從而提升對請求結果的理解。
  3. 身分驗證:探討基本驗證的實作,並針對 Base64 編碼進行介紹,了解其在資料傳輸中的應用和潛在的安全風險。

HTTP

請求方法

  • HTTP 請求方法:常見的 HTTP 請求方法包括 GET、POST、PUT、DELETE 等,它們分別代表不同的操作。

想像一下,HTTP 請求方法就像是你在餐廳點餐時,對服務生說的不同指令。

每種方法都有特定的用途,就像你點不同的菜一樣。以下是常見 HTTP 方法的簡單解釋:

GET(拿取)

  • 就像說:「我要看菜單」
  • 用途:向網站要資料
  • 特點:
    • 不會改變網站上的任何東西
    • 可以被收藏起來(加入書籤)
    • 適合分享給別人(因為資訊都在網址上)
GET 的資安問題
  • 敏感資料暴露
  • 問題描述
    • GET 請求會將所有的參數附加在 URL 中
    • 敏感資料(如密碼、信用卡號等)可能會暴露在瀏覽器歷史記錄、伺服器日誌中,甚至可能被第三方監控工具截取
  • 解決方案
    • 應該避免在 GET 請求中傳送敏感資料
    • 改用 POST 或其他更安全的方式進行資料傳輸

POST(寄送)

  • 就像說:「我要點這個餐」
  • 用途:向網站傳送新資料
  • 特點:
    • 可能會改變網站上的東西(例如新增一筆訂單)
    • 適合傳送隱私資料(因為資料不會顯示在網址上)
    • 可以傳送大量資料

PUT(放置)

  • 就像說:「幫我換成這個餐」
  • 用途:更新網站上已有的資料
  • 特點:
    • 會完全取代原本的資料
    • 如果原本沒有資料,可能會新增一筆

DELETE(刪除)

  • 就像說:「我要取消這個餐」
  • 用途:刪除網站上的資料
  • 特點:
    • 會移除指定的資料
    • 要小心使用,因為資料可能會不見喔!

PATCH(修補)

  • 就像說:「幫我加一點醬料就好」
  • 用途:部分更新資料
  • 特點:
    • 只更新指定的部分,不會動到其他地方
    • 比 PUT 更精確,只改要改的地方

HEAD(查看)

  • 就像說:「這道菜大概有多少?」
  • 用途:只問網站要資料的描述,不要實際內容
  • 特點:
    • 回應和 GET 一樣,但不會給你實際的內容
    • 可以用來確認資料是否存在,或者檢查更新時間

OPTIONS(詢問選項)

  • 就像說:「你們店裡有提供哪些服務?」
  • 用途:問網站支援哪些方法
  • 特點:
    • 常用於檢查網站的設定
    • 對跨來源請求(CORS)很重要

使用時機

  1. 想拿資料:用 GET
  2. 要傳新資料:用 POST
  3. 要更新全部資料:用 PUT
  4. 要更新部分資料:用 PATCH
  5. 要刪除資料:用 DELETE
  6. 只想知道資料標頭:用 HEAD
  7. 想知道網站能做什麼:用 OPTIONS

注意

  • GET 很方便,但別用它傳送隱私資料!
  • POST 適合傳送隱私資料,像是密碼之類的
  • 用 DELETE 之前要三思,資料刪掉就沒了
  • 如果不確定要用哪個,POST 通常最安全

資安問題: 未經授權的 HTTP 方法

  • 問題描述
    • 如果伺服器未正確設定,可能允許使用不安全的 HTTP 方法(如 PUT 或 DELETE)進行未經授權的操作,這可能導致資料被刪除或篡改。
  • 解決方案
    • 應該限制伺服器僅允許需要的 HTTP 方法
    • 並在伺服器端進行適當的權限檢查

Node.js lab 了解目前送出的請求方法

  • 新增資料夾 routes
  • 在 routes 新增 httpHandlers.js
// routes/httpHandlers.js

// 處理所有 HTTP 方法的路由
app.all('/method', (req, res) => {
  const method = req.method;
  res.send(`The HTTP method used was: ${method}`);
});
  • 在 server.js
const { handleMethod } = require('./routes/httpHandlers');

// 處理所有 HTTP 方法的路由
app.all('/method', handleMethod);
  • 可針對 /method 送請求,回應使用的請求方法。
  • app.all() 方法如何處理所有類型的 HTTP 請求。

實作: curl -X

image

curl -X [HTTP_METHOD] [URL]

其中,[HTTP_METHOD] 是你想要使用的 HTTP 方法,[URL] 是你要請求的網址。

  • GET 請求
curl http://nodelab.feifei.tw/method/api/resource

GET 是預設的方法,所以不需要 -X 參數。

  • POST 請求
curl -X POST -d "param1=value1&param2=value2" http://nodelab.feifei.tw/method/api/resource

-d 參數用來發送 POST 資料。

  • PUT 請求
curl -X PUT -d "param1=value1&param2=value2" http://nodelab.feifei.tw/method/api/resource
  • DELETE 請求
curl -X DELETE http://nodelab.feifei.tw/method/api/resource
  • PATCH 請求
curl -X PATCH -d "param1=value1" http://nodelab.feifei.tw/method/api/resource
  • HEAD 請求
curl -I http://nodelab.feifei.tw/method

-I 參數相當於發送 HEAD 請求。

  • OPTIONS 請求
curl -X OPTIONS http://nodelab.feifei.tw/method

HTTP 回應狀態碼

HTTP 狀態碼是伺服器對客戶端請求的回應,用於表示請求處理的結果。了解這些狀態碼有助於我們判斷請求的處理情況,並找出潛在的問題。

狀態碼分類

HTTP 狀態碼分為五類,每類都有其特定的意義:

  1. 1xx(資訊回應):請求已被接受,需要繼續處理
  2. 2xx(成功回應):請求已成功被伺服器接收、理解、並接受
  3. 3xx(重定向):需要後續操作才能完成請求
  4. 4xx(客戶端錯誤):請求包含語法錯誤或無法完成請求
  5. 5xx(伺服器錯誤):伺服器在處理請求的過程中發生了錯誤

1xx - 資訊回應

  • 100 Continue:伺服器收到了請求的起始部分,並且願意接受後續的請求。
  • 101 Switching Protocols:伺服器理解並同意客戶端的協議轉換請求。

2xx - 成功回應

  • 200 OK:請求成功。
  • 201 Created:請求成功並且伺服器創建了新的資源。
  • 204 No Content:伺服器成功處理了請求,但不需要返回任何實體內容。

3xx - 重定向

  • 301 Moved Permanently:請求的資源已被永久移動到新位置。
  • 302 Found:請求的資源臨時從不同的 URI 響應請求。
  • 304 Not Modified:資源未被修改,可以使用客戶端緩存的版本。

4xx - 客戶端錯誤

  • 400 Bad Request:伺服器不理解請求的語法。
  • 401 Unauthorized:請求需要使用者驗證。
  • 403 Forbidden:伺服器理解請求但拒絕執行。
  • 404 Not Found:伺服器找不到請求的資源。
  • 405 Method Not Allowed:請求方法不被允許。
  • 429 Too Many Requests:使用者在給定的時間內發送了太多請求。

5xx - 伺服器錯誤

  • 500 Internal Server Error:伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。
  • 502 Bad Gateway:作為網關或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應。
  • 503 Service Unavailable:由於臨時的伺服器維護或者過載,伺服器當前無法處理請求。
  • 504 Gateway Timeout:作為網關或者代理工作的伺服器嘗試執行請求時,未能及時從上游伺服器收到響應。

狀態碼的重要性

  1. 診斷問題:狀態碼可以幫助開發者快速識別和診斷問題。
  2. 優化使用者體驗:了解狀態碼可以幫助開發者設計更好的錯誤處理機制,提升使用者體驗。
  3. SEO 優化:某些狀態碼(如 301、404)對 SEO 有直接影響。
  4. 安全性:某些狀態碼(如 401、403)與安全性相關,正確使用可以提高應用的安全性。

實際應用

在開發 Web 應用時,正確使用 HTTP 狀態碼可以:

  1. 幫助使用者端識別問題,處理不同情況。
  2. 提供更清晰的錯誤訊息,方便 debug。
  3. 遵循 RESTful API 設計原則,使 API 更加規範和易於理解。

狀態碼對應圖

實作練習與了解

  • 進入資料夾 routes
  • 在 routes 修改 httpHandlers.js
// routes/httpHandlers.js

app.all('/status/:code', (req, res) => {
  const code = parseInt(req.params.code);
  const message = getStatusMessage(code);
  res.status(code).json({ code, message });
});

// 輔助函數:取得狀態碼對應的訊息
function getStatusMessage(code) {
  const messages = {
      // 1xx 訊息
      100: '繼續處理中',
      101: '切換協定',
      102: '處理中', // RFC 2518 WebDAV 擴充

      // 2xx 成功
      200: '請求成功',
      201: '資源已建立',
      202: '已接受處理,但尚未完成',
      204: '無內容,無需回應',
      206: '部分內容', // 針對範圍請求

      // 3xx 重定向
      300: '多種選擇',
      301: '資源永久移動',
      302: '資源暫時移動',
      303: '請查看其他位置',
      304: '資源未修改',
      307: '暫時重定向',
      308: '永久重定向',

      // 4xx 客戶端錯誤
      400: '錯誤的請求',
      401: '未授權,請登入',
      403: '禁止存取',
      404: '找不到資源',
      405: '不允許的請求方法',
      406: '不接受的回應格式',
      408: '請求超時',
      409: '資源衝突',
      410: '資源已永久移除',
      429: '請求次數過多,請稍後再試',

      // 5xx 伺服器錯誤
      500: '伺服器內部錯誤',
      501: '功能尚未實作',
      502: '錯誤的網關',
      503: '服務目前無法使用',
      504: '網關超時',
      505: '不支援的 HTTP 版本'
      
  };
  return messages[code] || '未知的狀態碼';
}
  • 在 server.js
const { handleMethod, handleStatus } = require('./routes/httpHandlers');


// 處理所有 HTTP 方法的路由
app.all('/method', handleMethod);

// 狀態碼處理路由
app.all('/status/:code', handleStatus);

image

使用 curl 實作請求

  • curl -i http://nodelab.feifei.tw/status/404

image
image

HTTP 標頭(Header)

HTTP 標頭是請求和回應中不可或缺的一部分,它們攜帶了關於傳輸、安全性、內容類型等重要資訊。然而,某些標頭可能會洩露敏感資訊,如伺服器版本,這些資訊可能被惡意使用者利用來進行攻擊。

在身分驗證的環境中,HTTP 標頭再次扮演了關鍵角色,特別是在基本認證(Basic Authentication)的實現中。

身分驗證

身分驗證是網站安全的核心環節之一,用於驗證使用者的身分,並根據其權限授予對應的資源訪問權。

常見的身分驗證方式列表

  1. Basic Authentication(基本驗證)
    • 採用 HTTP 協定的基本驗證機制
    • 透過 Authorization 標頭傳送 base64 編碼的帳號和密碼
  2. 帳號密碼驗證
    • 最傳統的驗證方式
    • 使用者輸入帳號和密碼進行驗證
  3. Token-Based Authentication(權杖驗證)
    • 使用權杖進行身分驗證
    • 常見實作包括 JWT(JSON Web Tokens)
  4. Session-Based Authentication(階段驗證)
    • 伺服器端儲存階段資訊
    • 客戶端透過 cookie 維護階段 ID
  5. OAuth 2.0
    • 用於授權的開放標準
    • 允許第三方應用程式存取使用者資源,無需知道使用者帳密
  6. OpenID Connect
    • 建立在 OAuth 2.0 之上的身分層
    • 提供標準化的方式來驗證使用者身分
  7. 多因素驗證 (MFA)
    • 結合多種驗證方式,例如密碼 + 手機驗證碼
    • 提高安全性,降低單點失效風險
  8. 生物辨識驗證
    • 使用指紋、臉部辨識、虹膜掃描等生物特徵
    • 常用於行動裝置和高安全性要求的場景
  9. 單一登入 (SSO)
    • 讓使用者使用單一憑證存取多個應用程式
    • 常見於企業環境或大型網路生態系統
  10. SAML (Security Assertion Markup Language)
    • 基於 XML 的開放標準資料格式
    • 用於身分提供者和服務提供者之間交換驗證和授權資料
  11. 社群媒體登入
    • 利用既有社群媒體帳號(如 Facebook、Google、Twitter)進行身分驗證
    • 通常基於 OAuth 或 OpenID Connect 實作
  12. 硬體權杖驗證
    • 使用專用硬體裝置產生一次性密碼
    • 常用於高安全性要求的企業或金融機構
  13. Magic Links(魔法連結)
    • 透過寄送含有特殊身分驗證連結的電子郵件進行驗證
    • 使用者點擊連結即可登入,無需輸入密碼

每種方法都有其優缺點和適用場景。在實際應用中,常常會結合多種方法來增強安全性並改善使用者體驗。選擇合適的驗證方式需要考慮安全需求、使用者友善度、技術實作難度等多個因素。

基本認證(Basic Authentication)

身分驗證是確保系統安全的重要環節。在 Web 應用程式中,基本認證是一種簡單但廣泛使用的方法。

實作

// routes/authHandler.js

// 模擬的使用者資料庫,實際應用中應該從資料庫查詢
const users = {
    'admin': 'password123',
    'user': 'userpass'
};

function authenticateBasic(req, res, next) {
    const authHeader = req.headers.authorization;
    
    if (!authHeader) {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:需要提供認證資訊。');
    }

    const auth = Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
    const user = auth[0];
    const pass = auth[1];

    if (users[user] && users[user] === pass) {
        next();
    } else {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:使用者名稱或密碼錯誤。');
    }
}

// 受保護的路由處理器
function protectedRoute(req, res) {
    res.send('歡迎來到受保護的區域!');
}

module.exports = {
    authenticateBasic,
    protectedRoute
};

程式碼解析

  1. 模擬使用者資料庫

    const users = {
        'admin': 'password123',
        'user': 'userpass'
    };
    

    這是一個簡單的物件,用於模擬使用者資料庫。在實際應用中,這應該被替換為真實的資料庫查詢。

  2. authenticateBasic 函數
    這是一個 Middleware 函數,用於處理基本認證。

  3. 檢查授權標頭

    const authHeader = req.headers.authorization;
    

    從請求標頭中取得 Authorization 欄位。

  4. 處理未提供認證資訊的情況

    if (!authHeader) {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:需要提供認證資訊。');
    }
    

    如果沒有提供認證資訊,設定 WWW-Authenticate 標頭並返回 401 狀態碼。

  5. 解碼和驗證認證資訊

    const auth = Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
    const user = auth[0];
    const pass = auth[1];
    

    解碼 base64 編碼的認證資訊,並分離使用者名稱和密碼。

  6. 驗證使用者

    if (users[user] && users[user] === pass) {
        next();
    } else {
        res.setHeader('WWW-Authenticate', 'Basic realm="Restricted Area"');
        return res.status(401).send('認證失敗:使用者名稱或密碼錯誤。');
    }
    

    檢查使用者名稱和密碼是否相同。如果相同,調用 next() 繼續處理請求;否則,返回 401 錯誤。

const { authenticateBasic, protectedRoute } = require('./routes/authHandler');
// 設定受保護的路由
app.get('/protected', authenticateBasic, protectedRoute);

這段程式碼將 authenticateBasic Middleware 應用到 /protected 路由,確保只有通過認證的使用者才能訪問。

資安問題:HTTP 基本驗證漏洞

  • 問題描述
    • HTTP 基本驗證只使用 Base64 將使用者名稱和密碼編碼,而不是加密
    • 如果未使用 HTTPS,這些編碼後的帳號密碼很容易被攔截並解碼。
  • 解決方案
    • 確保使用 HTTPS 保護基本驗證過程中的資料傳輸,並且應考慮使用更安全的身份驗證方法,如 OAuth 或 JWT。

實作: 開啟瀏覽器

步驟一: 開啟瀏覽器與開發者工具
  • 開啟瀏覽器 http://nodelab.feifei.tw/protected
    存取內容跳出使用者帳號密碼輸入框
  • 送出帳號密碼之前先開啟開發者工具觀察
步驟二: 送出帳號密碼之後觀察標頭
  • 觀察請求封包中有標頭 Authorization
    image
步驟三: 使用 curl 觀察
  • 使用 curl -v http://nodelab.feifei.tw/protected
  • 狀態碼 401 Unauthorized 狀態碼
    • 表示請求需要認證
  • WWW-Authenticate 標頭
    • 使用者端應該使用 HTTP Basic 認證
      image
步驟四: 使用 curl -H 觀察
  • 使用 curl -H 指定標頭
  • curl -H "Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=" -v http://nodelab.feifei.tw/protected
    image

步驟五: 解開 base64

YWRtaW46cGFzc3dvcmQxMjM=
image

base64

Base64 是一種用來將二進位資料轉換成文字的編碼方式,主要使用 64 個可列印的字元來表示資料,這使得資料可以安全地在需要純文字傳輸的環境中傳送(例如在 URL、電子郵件等情況下)。

Base64 是什麼

Base64 是一種將數位資料編碼成文字的技術。你可以把它想像成一個翻譯器,它把電腦使用的 0 和 1(也就是位元資料)轉換成我們能夠閱讀的字母和符號。

Base64 如何運作

Base64 編碼的基本原理是:

  1. 把文字轉換為電腦語言(ASCII 編碼):例如,字母 "M" 會被轉換成 ASCII 值 77。
  2. 把這些數字轉換為位元:每個 ASCII 編碼都會變成一連串的 0 和 1。
  3. 將位元分組:每 6 個位元被組成一個新的組合,這些新的組合會對應到 Base64 表中的字母或符號。
  4. 轉換結果:例如,"Man" 會被轉換為 Base64 編碼 "TWFu"。

特徵與應用

  • Base64 編碼常見的特徵是最後會出現一個或兩個等號 "=", 這表示原始資料的長度不足,需要填補才能完成編碼。
  • 常見的使用場景包括:在 HTTP 網路請求中,當一個網站需要進行用戶名和密碼認證時,會把這些資訊進行 Base64 編碼。

例子

  • 文字 "Hello" 在 Base64 中會變成 "SGVsbG8="。
  • 網站上的一組用戶名和密碼 "guest:guest" 經過 Base64 編碼後會變成 "Z3Vlc3Q6Z3Vlc3Q="。

為什麼需要等號

當需要編碼的資料長度不是 3 的倍數時,Base64 會在結果中加上等號來表示這個資料的結尾。這樣可以確保編碼結果是完整且準確的。

這樣的方式可以幫助資料在不同系統之間傳輸時保持一致性,並且避免因為某些系統只接受文字格式而產生的問題。

Base64 誤用的資安問題

  • 新聞媒體誤用編碼,右邊的民眾檢舉人代號
  • 可以快速利用工具解碼回台灣的身分證字號
    image
    image

總結

HTTP 請求方法

  1. GET:請求資料,不改變伺服器狀態,可能暴露敏感資料。
  2. POST:提交新資料,適合傳送隱私資訊。
  3. PUT:更新資料,會完全取代原有資料。
  4. DELETE:刪除資料,需謹慎使用。
  5. PATCH:部分更新資料。
  6. HEAD:獲取資料標頭,不返回內容。
  7. OPTIONS:查詢支持的方法。

HTTP 狀態碼

  • 1xx(資訊回應):如 100 Continue
  • 2xx(成功):如 200 OK、201 Created
  • 3xx(重定向):如 301 Moved Permanently
  • 4xx(客戶端錯誤):如 404 Not Found、401 Unauthorized
  • 5xx(伺服器錯誤):如 500 Internal Server Error

身分驗證

  • 基本驗證:使用 Base64 編碼的帳號密碼,需透過 HTTPS 保護,目前已棄用。
  • Token-Based、Session-Based、OAuth、MFA 等:各有優缺點及適用情境。

Base64

  • 編碼技術
  • 常常有人誤用

選擇題

  1. HTTP 請求方法中,哪一種方法適合用來提交敏感資料?

    • A) GET
    • B) POST
    • C) DELETE
    • D) HEAD

    答案: B) POST

  2. 哪一個狀態碼代表請求成功?

    • A) 404
    • B) 500
    • C) 200
    • D) 301

    答案: C) 200

  3. 在基本驗證中,使用者的帳號和密碼是如何傳送的?

    • A) 明文
    • B) Base64 編碼
    • C) SHA-256 雜湊
    • D) AES 加密

    答案: B) Base64 編碼

  4. HTTP 請求方法中,哪一種方法用於查詢伺服器支援的 HTTP 方法?

    • A) OPTIONS
    • B) GET
    • C) POST
    • D) DELETE

    答案: A) OPTIONS

  5. 以下哪一項是 Base64 編碼的特徵?

    • A) 不使用等號
    • B) 總是返回固定長度
    • C) 最後可能出現一個或兩個等號
    • D) 只用於文字資料傳輸

    答案: C) 最後可能出現一個或兩個等號

整理實作

  1. Node.js HTTP 方法測試
    • 建立 httpHandlers.js,處理所有 HTTP 方法。
    • 使用 app.all('/method', handleMethod) 接收請求並回應使用的請求方法。
  2. 使用 curl 測試請求
    • GET: curl http://nodelab.feifei.tw/method
    • POST: curl -X POST -d "param1=value1&param2=value2" http://nodelab.feifei.tw/method
    • PUT: curl -X PUT -d "param1=value1&param2=value2" http://nodelab.feifei.tw/method
    • DELETE: curl -X DELETE http://nodelab.feifei.tw/method
    • PATCH: curl -X PATCH -d "param1=value1" http://nodelab.feifei.tw/method
    • HEAD: curl -I http://nodelab.feifei.tw/method
    • OPTIONS: curl -X OPTIONS http://nodelab.feifei.tw/method
  3. HTTP 狀態碼測試
    • 修改 httpHandlers.js 以處理狀態碼請求。
    • 使用 curl -i http://nodelab.feifei.tw/status/404 測試。
  4. 基本認證實作
    • authHandler.js 中實作基本驗證。
    • 設定受保護路由:app.get('/protected', authenticateBasic, protectedRoute);
  5. Base64 編碼與解碼
    • 使用工具解碼 Base64 字串,並檢查編碼過程。
  6. 撰寫練習心得與想法

上一篇
資安這條路:Day 4 深入理解 HTTP 協定中的 CRLF 與漏洞實作
下一篇
資安這條路:Day6 資料庫安全與使用者管理系統實作、API 安全
系列文
資安這條路:系統化學習網站安全與網站滲透測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言